using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Principal;

namespace RandomSearch.PluginsCommon {
    public abstract class BaseSearchPlugin : ISearchPlugin {
        protected readonly Func<IdentityReference, bool> _idChecker;

        protected BaseSearchPlugin() {
            _idChecker = SecurityHelper.GetMemoizedIdentityChecker(WindowsIdentity.GetCurrent());
        }

        #region ISearchPlugin Members

        public abstract string PluginId { get; }

        public abstract string PluginName { get; }

        public abstract string PatternToSearch { get; }

        public virtual IEnumerable<SearchResult> Search(SearchOptions options) {
            SearchOptions o = options;
            return Search(o, _idChecker, PatternToSearch);
        }

        public abstract string Options { get; }

        #endregion

        private static IEnumerable<SearchResult> Search(SearchOptions options,
                                                        Func<IdentityReference, bool> identityChecker, string pattern) {
            var toProcess = new Queue<DirectoryInfo>();
            toProcess.Enqueue(new DirectoryInfo(options.PathToRootFolder));

            while (toProcess.Count > 0) {
                DirectoryInfo curr = toProcess.Dequeue();

                IEnumerable<FileInfo> files = curr.EnumerateFiles(pattern);
                IEnumerable<FileInfo> accessiblef = files.Where(x => FSACLHelper.CanReadFile(x, identityChecker));
                foreach (FileInfo file in accessiblef) {
                    if (Match(options, file, identityChecker))
                        yield return new SearchResult(file);
                }

                if (!options.IncludeSubfolders) continue;
                IEnumerable<DirectoryInfo> sub = curr.EnumerateDirectories("*", SearchOption.TopDirectoryOnly);
                List<DirectoryInfo> accessible =
                    sub.Where(x => FSACLHelper.CanReadDirectory(x, identityChecker)).ToList();
                foreach (DirectoryInfo d in accessible) toProcess.Enqueue(d);
            }
        }

        private static bool Match(SearchOptions options, FileInfo fi, Func<IdentityReference, bool> identityChecker) {
            bool bRes = true;
            if (options.MaxSize.HasValue)
                bRes = bRes && (fi.Length <= options.MaxSize.Value);
            if (options.MinSize.HasValue)
                bRes = bRes && (fi.Length >= options.MinSize.Value);
            if (options.DateTo.HasValue)
                bRes = bRes && (fi.CreationTime <= options.DateTo);
            if (options.DateFrom.HasValue)
                bRes = bRes && (fi.CreationTime >= options.DateFrom);
            if (options.RequiredAttributes.HasValue)
                bRes = bRes && ((fi.Attributes & options.RequiredAttributes.Value) == options.RequiredAttributes.Value);
            return bRes;
        }
    }
}